//
//  Ch3_3Controller.swift
//  learn-gl-es-swift
//
//  Created by LimitLiu on 2019/9/10.
//  Copyright © 2019 LimitLiu. All rights reserved.
//

import UIKit
import GLKit
import YogaKit

struct SceneVertex3_3 {
    var positionCoords: GLKVector3
    var textureCoords: GLKVector2
}

class Ch3_3Controller: GLKViewController, GLKViewControllerDelegate {

    var baseEffect = GLKBaseEffect()
    var vertexBuffer: GLVertexAttribArrayBuffer!
    var shouldUseLinearFilter: Bool = false
    var shouldAnimate = true
    var shouldRepeatTexture = true
    var coordinateOffset: GLfloat = GLfloat()
    
    var vertices: [SceneVertex3_3] = [
        SceneVertex3_3(positionCoords: vec(-0.5, -0.5, 0), textureCoords: vec(0, 0)),
        SceneVertex3_3(positionCoords: vec(0.5, -0.5, 0), textureCoords: vec(1, 0)),
        SceneVertex3_3(positionCoords: vec(-0.5, 0.5, 0), textureCoords: vec(0, 1)),
    ]
    
    let defaultVertices: [SceneVertex3_3] = [
        SceneVertex3_3(positionCoords: vec(-0.5, -0.5, 0), textureCoords: vec(0, 0)),
        SceneVertex3_3(positionCoords: vec(0.5, -0.5, 0), textureCoords: vec(1, 0)),
        SceneVertex3_3(positionCoords: vec(-0.5, 0.5, 0), textureCoords: vec(0, 1)),
    ]
    
    var movementVectors: [GLKVector3] = [
        vec(-0.02, -0.01, 0),
        vec(0.01, -0.005, 0),
        vec(-0.01, 0.01, 0),
    ]
    
    deinit {
        let view = self.view as! GLKView
        GLContext.setCurrent(view.context)
        vertexBuffer = nil
        GLContext.setCurrent(nil)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.delegate = self
        let view = self.view as! GLKView
        view.context = GLContext(api: .openGLES2)!
        GLContext.setCurrent(view.context)
        baseEffect.useConstantColor = GLboolean(GL_TRUE)
        baseEffect.constantColor = vec(1, 1, 1, 1)
        (view.context as! GLContext).clearColor = vec(0, 0, 0, 1)
        
        vertexBuffer = GLVertexAttribArrayBuffer(
            attribStride: GLsizei(MemoryLayout<SceneVertex3_3>.stride),
            numberOfVertices: GLsizei(vertices.count),
            data: vertices,
            usage: GLenum(GL_DYNAMIC_DRAW)
        )
        
        if let imageRef = UIImage(named: "grid")?.cgImage {
            let textureInfo = try! GLTextureLoader.texture(image: imageRef, options: nil)
            baseEffect.texture2d0.name = textureInfo.name
            baseEffect.texture2d0.target = GLKTextureTarget(rawValue: textureInfo.target)!
        } else {
            fatalError("Can't to load the image.")
        }
        
        view.configureLayout { v in
            v.isEnabled = true
        }
        doLayout()
    }
    
    private func doLayout() {
        let myView = UIView(frame: .zero)
        myView.backgroundColor = .white
        myView.configureLayout { v in
            v.isEnabled = true
            v.flexDirection = .column
            v.position = .absolute
            v.bottom = 0
            v.left = 0
            v.width = 100%
            v.paddingBottom = 10
        }
        
        let mySlider = UISlider(frame: .zero)
        mySlider.tintColor = .red
        mySlider.addTarget(self, action: #selector(doSlider(_:)), for: .valueChanged)
        mySlider.configureLayout { (slider: YGLayout) in
            slider.isEnabled = true
            slider.width = 100%
        }
        
        let line0 = UIView(frame: .zero)
        let line1 = UIView(frame: .zero)
        let line2 = UIView(frame: .zero)
        let line3 = UIView(frame: .zero)

        let lineLayout = { (v: YGLayout) in
            v.isEnabled = true
            v.flexGrow = 1
            v.flexDirection = .row
            v.paddingVertical = 10
            v.paddingHorizontal = 10
        }
        
        line0.configureLayout {
            lineLayout($0)
            $0.justifyContent = .center
            $0.alignItems = .center
        }
        line1.configureLayout(block: lineLayout)
        line2.configureLayout(block: lineLayout)
        line3.configureLayout(block: lineLayout)
        
        let uiSwitch1 = UISwitch(frame: .zero)
        let uiSwitch2 = UISwitch(frame: .zero)
        let uiSwitch3 = UISwitch(frame: .zero)
        
        uiSwitch1.tintColor = .red
        uiSwitch1.isOn = shouldAnimate
        uiSwitch1.addTarget(self, action: #selector(doAnimate(_:)), for: .valueChanged)
        
        uiSwitch2.tintColor = .red
        uiSwitch2.isOn = shouldUseLinearFilter
        uiSwitch2.addTarget(self, action: #selector(doFilter(_:)), for: .valueChanged)
        
        uiSwitch3.tintColor = .red
        uiSwitch3.isOn = shouldRepeatTexture
        uiSwitch3.addTarget(self, action: #selector(doRepeat(_:)), for: .valueChanged)

        let switchLayout = { (s: YGLayout) in
            s.isEnabled = true
        }
        
        uiSwitch1.configureLayout(block: switchLayout)
        uiSwitch2.configureLayout(block: switchLayout)
        uiSwitch3.configureLayout(block: switchLayout)
        
        let labelLayout = { (l: YGLayout) in
            l.isEnabled = true
            l.height = 30
            l.marginLeft = 10
        }
        let lbl1 = UILabel(frame: .zero)
        let lbl2 = UILabel(frame: .zero)
        let lbl3 = UILabel(frame: .zero)
        
        lbl1.text = "do animate"
        lbl2.text = "do filter"
        lbl3.text = "do repeat"
        
        lbl1.configureLayout(block: labelLayout)
        lbl2.configureLayout(block: labelLayout)
        lbl3.configureLayout(block: labelLayout)
        
        line0.addSubview(mySlider)
        
        line1.addSubview(uiSwitch1)
        line1.addSubview(lbl1)
        
        line2.addSubview(uiSwitch2)
        line2.addSubview(lbl2)
        
        line3.addSubview(uiSwitch3)
        line3.addSubview(lbl3)
        
        myView.addSubview(line0)
        myView.addSubview(line1)
        myView.addSubview(line2)
        myView.addSubview(line3)

        view.addSubview(myView)
        view.yoga.applyLayout(preservingOrigin: false)
    }
    
    @objc func doAnimate(_ sender: UISwitch) {
        shouldAnimate = sender.isOn
    }
    
    @objc func doFilter(_ sender: UISwitch) {
        shouldUseLinearFilter = sender.isOn
    }
    
    @objc func doRepeat(_ sender: UISwitch) {
        shouldRepeatTexture = sender.isOn
    }
    
    @objc func doSlider(_ sender: UISlider) {
        coordinateOffset = sender.value
    }
    
    func updateAnimatedVertexPosition() {
        if shouldAnimate {
            (0..<3).forEach {
                vertices[$0].positionCoords.x += movementVectors[$0].x
                if vertices[$0].positionCoords.x >= 1 || vertices[$0].positionCoords.x <= -1 {
                    movementVectors[$0].x = -movementVectors[$0].x
                }
                vertices[$0].positionCoords.y += movementVectors[$0].y
                if vertices[$0].positionCoords.y >= 1 || vertices[$0].positionCoords.y <= -1 {
                    movementVectors[$0].y = -movementVectors[$0].y
                }
                vertices[$0].positionCoords.z += movementVectors[$0].z
                if vertices[$0].positionCoords.z >= 1 || vertices[$0].positionCoords.z <= -1 {
                    movementVectors[$0].z = -movementVectors[$0].z
                }
            }
        } else {
            (0..<3).forEach {
                vertices[$0].positionCoords.x = defaultVertices[$0].positionCoords.x
                vertices[$0].positionCoords.y = defaultVertices[$0].positionCoords.y
                vertices[$0].positionCoords.z = defaultVertices[$0].positionCoords.z
            }
        }
        
        (0..<3).forEach {
            vertices[$0].textureCoords.s = defaultVertices[$0].textureCoords.s + coordinateOffset
        }
    }
    
    func updateTextureParameters() {
        baseEffect.texture2d0.set(
            parameterId: GLenum(GL_TEXTURE_WRAP_S),
            value: (shouldRepeatTexture ? GL_REPEAT : GL_CLAMP_TO_EDGE)
        )
        baseEffect.texture2d0.set(
            parameterId: GLenum(GL_TEXTURE_MAG_FILTER),
            value: (shouldUseLinearFilter ? GL_LINEAR : GL_NEAREST)
        )
    }
    
    func glkViewControllerUpdate(_ controller: GLKViewController) {
        updateAnimatedVertexPosition()
        updateTextureParameters()
        vertexBuffer.reinit(
            attribStride: GLsizei(MemoryLayout<SceneVertex3_3>.stride),
            numberOfVertices: GLsizei(vertices.count),
            data: vertices
        )
    }
    
    override func glkView(_ view: GLKView, drawIn rect: CGRect) {
        baseEffect.prepareToDraw()
        (view.context as! GLContext).clear(GLbitfield(GL_COLOR_BUFFER_BIT))
        
        vertexBuffer.prepareToDraw(
            attrib: GLuint(GLKVertexAttrib.position.rawValue),
            numberOfCoordinates: 3,
            attribOffset: GLsizeiptr(0),
            shouldEnable: true
        )
        
        vertexBuffer.prepareToDraw(
            attrib: GLuint(GLKVertexAttrib.texCoord0.rawValue),
            numberOfCoordinates: 2,
            attribOffset: GLsizeiptr(MemoryLayout<GLfloat>.size * 4),
            shouldEnable: true
        )
        vertexBuffer.drawArray(
            mode: GLenum(GL_TRIANGLES),
            startVertexIdx: 0,
            numberOfVertices: GLsizei(vertices.count)
        )
    }

}
